Skip to content

Ingress Nginx的奇巧淫技

前提:在集群部署好 Nginx Ingress

Redirect

redirect 主要用于域名重定向,比如访问 a.com 被重定向到 b.com。

如下我们配置访问 ng.jokerbai.com 重定向到 www.baidu.com

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/permanent-redirect: "https://www.baidu.com"
spec:
  rules:
    - host: ng.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: ng-svc
              servicePort: 80

然后浏览器访问 ng.jokerbai.com 就会被重定向到 https://www.baidu.com

yaml
PS E:\DEV\Go\src\code.rookieops.com\coolops> curl.exe -I http://ng.jokerbai.com:30369
HTTP/1.1 301 Moved Permanently
Server: nginx/1.17.10
Date: Tue, 12 May 2020 09:06:29 GMT
Content-Type: text/html
Content-Length: 170
Connection: keep-alive
Location: https://www.baidu.com

Rewrite

rewrite 主要用于地址重写,比如访问 a.com/foo 重写到 a.com,访问 a.com/foo/bbb 重写到 a.com/bbb

比如下面例子:

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: "/$1"
spec:
  rules:
    - host: ng.jokerbai.com
      http:
        paths:
          - path: /foo/?(.*)
            backend:
              serviceName: ng-svc
              servicePort: 80

927fd1577ab68c311859eadaa09150da MD5

匹配请求头

匹配请求头,主要用于根据请求头信息将用户请求转发到不同的应用,比如根据不同的客户端转发请求。

如下:

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/server-snippet: |
      set $agentflag 0;

      if ($http_user_agent ~* "(iPhone)" ){
        set $agentflag 1;
      }

      if ( $agentflag = 1 ) {
        return 301 http://iphone.jokerbai.com:30369;
      }
spec:
  rules:
    - host: ng.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: ng-svc
              servicePort: 80

然后再创建一个 iphone.jokerbai.com 的 ingress,我这里只是测试就随便定义了,如下:

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: iphone.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: ng-svc
              servicePort: 80

我现在以 iPad 访问,我们发现起没有被 301 重定向。

4e091518ddfa8ed96fe5ab3a89ec2997 MD5

当我们使用 iPhone 访问的时候,可以发现起被 301 重定向了

3ab10b584ba268920dac81d185ff14f7 MD5

认证访问

有些访问是需要认证访问的,比如 dubbo-admin,我们在访问的时候会先叫你输入用户名和密码。ingress nginx 也可以实现这种。

(1)、创建密码,我这里用 http 的命令工具来生成

yaml
[root@k8s-master ingress-nginx]# htpasswd -c auth joker
New password:
Re-type new password:
Adding password for user joker
[root@k8s-master ingress-nginx]# cat auth
joker:$apr1$R.G4krs/$hh0mX8xe4A3lYKMjvlVs1/

(2)、创建 secret

yaml
[root@k8s-master ingress-nginx]# kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created

(3)、配置 Ingress

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
spec:
  rules:
    - host: iphone.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: ng-svc
              servicePort: 80

a9533c5a13e7a75540911358310ea8d5 MD5

黑白名单

在配置 NG 的时候可以用 allow 和 deny 来配置黑白名单,在 ingress nginx 上也可以设置黑白名单。

(1)、白名单

添加白名单的方式可以直接写 annotation,也可以配置在 ConfigMap 中。

写在 annotation 中:

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/whitelist-source-range: 10.1.10.2
spec:
  rules:
    - host: iphone.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: ng-svc
              servicePort: 80

也可以写固定 IP,也可以写网段。

配置到 ConfigMap 中:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    helm.sh/chart: ingress-nginx-2.1.0
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.32.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  whitelist-source-range: 10.1.10.0/24

然后重新 apply 一下就 OK

(2)、黑名单

黑名单就只能通过 ConfigMap 来配置了,配置如下:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    helm.sh/chart: ingress-nginx-2.1.0
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.32.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  whitelist-source-range: 10.1.10.0/24
  block-cidrs: 10.1.10.100

速率限制

可以限制速率来降低后端压力,比如如下配置:

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/limit-rate: "100K"
    nginx.ingress.kubernetes.io/limit-whitelist: "10.1.10.100"
    nginx.ingress.kubernetes.io/limit-rps: "1"
    nginx.ingress.kubernetes.io/limit-rpm: "30"
spec:
  rules:
    - host: iphone.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: ng-svc
              servicePort: 80

其中:

  • nginx.ingress.kubernetes.io/limit-rate:限制客户端每秒传输的字节数
  • nginx.ingress.kubernetes.io/limit-whitelist:白名单中的 IP 不限速
  • nginx.ingress.kubernetes.io/limit-rps:单个 IP 每秒的连接数
  • nginx.ingress.kubernetes.io/limit-rpm:单个 IP 每分钟的连接数

更多请点这里

灰度发布

Nginx Annotations 支持以下 4 种 Canary 规则:

  • nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never时,请求不会被发送到 Canary 入口;对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他金丝雀规则进行优先级的比较。
  • nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。该规则允许用户自定义 Request Header 的值,必须与上一个 annotation (即:canary-by-header)一起使用。
  • nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求。权重为 100 意味着所有请求都将被发送到 Canary 入口。
  • nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的 cookie。当 cookie 值设置为 always时,它将被路由到 Canary 入口;当 cookie 值设置为 never时,请求不会被发送到 Canary 入口;对于任何其他值,将忽略 cookie 并将请求与其他金丝雀规则进行优先级的比较。

定义两个版本的代码。

代码 v1

yaml
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main(){
	g:=gin.Default()
	g.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"version": "v1",
			"data": "hello world",
		})

		})
	_ = g.Run("8080")
}

代码 v2

yaml
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main(){
	g:=gin.Default()
	g.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"version": "v2",
			"data": "hello world,SB",
		})

		})
	_ = g.Run("8080")
}

然后制作镜像,这里我们正好可以练习下多阶段构建...

Dockerfile 如下:

yaml
FROM golang AS build-env
ADD . /go/src/app
WORKDIR /go/src/app
RUN go get -u -v github.com/gin-gonic/gin
RUN govendor sync
RUN GOOS=linux GOARCH=386 go build -v -o /go/src/app/app-server-v1

FROM alpine
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime
COPY --from=build-env /go/src/app/app-server-v1 /usr/local/bin/app-server-v1
EXPOSE 8080
CMD [ "/usr/local/bin/app-server-v1" ]

构建镜像

yaml
docker build -t registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1 .

上传镜像

yaml
docker push registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1

v2 版本相同操作

v1 版本的 deploy 和 svc

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-server-v1
spec:
  selector:
    matchLabels:
      app: app-server-v1
  replicas: 2
  template:
    metadata:
      labels:
        app: app-server-v1
    spec:
      containers:
        - name: app-server-v1
          image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: app-server-v1-svc
spec:
  selector:
    app: app-server-v1
  ports:
    - name: http
      port: 8080

v2 版本 deploy 和 svc

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-server-v2
spec:
  selector:
    matchLabels:
      app: app-server-v2
  replicas: 2
  template:
    metadata:
      labels:
        app: app-server-v2
    spec:
      containers:
        - name: app-server-v2
          image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v2
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: app-server-v2-svc
spec:
  selector:
    app: app-server-v2
  ports:
    - name: http
      port: 8080

运行两个版本进行测试:

yaml
[root@k8s-master ingress-nginx]# curl 10.103.133.234:8080
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl 10.101.125.220:8080
{"data":"hello SB","version":"v2"}

然后创建 ingress。

(1)、正常版本(v1 版本)

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: ng.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: app-server-v1
              servicePort: 8080

部署后访问

0b065200b54cbe541038eb826b698386 MD5

(2)、v2 版本

yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
    - host: ng.jokerbai.com
      http:
        paths:
          - path:
            backend:
              serviceName: app-server-v2-svc
              servicePort: 8080

说明:

  • nginx.ingress.kubernetes.io/canary: true 表示开启 canary
  • nginx.ingress.kubernetes.io/canary-weight: 10 表示权重为 10,也就是 v1:v2 大致为 9:1

然后部署后进行测试:

yaml
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello world","version":"v1"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello SB","version":"v2"}
[root@k8s-master ingress-nginx]# curl http://ng.jokerbai.com:30369
{"data":"hello SB","version":"v2"}

可以看到上述的这些操作基本都用到 annotation,更多的可以点击官方文档

最近更新